﻿using System;
using System.Threading.Tasks;
using System.Windows;

using Outcoder.Concurrency;
using Outcoder.Data;
using Outcoder.InversionOfControl;
using Outcoder.InversionOfControl.Containers.SimpleContainer;
using Outcoder.LauncherModel.Launchers;
using Outcoder.Logging;
using Outcoder.Logging.Loggers;
using Outcoder.Messaging;
using Outcoder.NavigationModel;
using Outcoder.Services;
using Outcoder.Services.Implementation;
using Outcoder.UserOptionsModel;

#if !WPF
using Outcoder.ComponentModel.StatePreservation;
#endif

namespace Outcoder.ApplicationModel
{
	/// <summary>
	/// This class initializes the IoC container with various core services.
	/// Subclass this class to override how the IoC container is created 
	/// and which service implementations are registered. 
	/// </summary>
	public class CalciumSystem
	{
#if !WPF
		readonly StateManager stateManager = new StateManager();
#endif
		readonly NavigationMonitor navigationMonitor = new NavigationMonitor();

		internal bool RestoreTransientState { get; set; }

		bool initialized;
		readonly object initializedLock = new object();

		public enum InitializationMode
		{
			Normal,
			UnitTesting
		}

		InitializationMode initializationMode;

		public virtual void Initialize(
			InitializationMode mode = InitializationMode.Normal, 
			IContainer container = null)
		{
			if (!initialized)
			{
				lock (initializedLock)
				{
					if (!initialized)
					{
						initialized = true;

						initializationMode = mode;

						if (container == null)
						{
							container = CreateContainer();

							if (container == null)
							{
								throw new Exception("Call to CreateContainer produced a null result.");
							}
						}

						container.InitializeContainer();

						Dependency.Register<CalciumSystem>(this);

						PopulateContainer();

#if !WPF
						/* The state manager performs automatic persistence of properties 
						 * when the app is tombstoned or terminated. 
						 * See the PersistencyExampleViewModel. */
						stateManager.Initialize();
#endif

						/* The navigation monitor lets the app's viewmodels know when 
						 * a page transition occurs, and allows the viewmodel 
						 * to cancel the navigation. 
						 * The ViewModelBase class contains the current query string. */
#if !WPF
			Task.Run(() => { navigationMonitor.Initialize(); });
#else
						Application.Current.Dispatcher.BeginInvoke(new Action(() => { navigationMonitor.Initialize(); }));
#endif
					}
				}
			}
		}

		protected virtual IContainer CreateContainer()
		{
			/* Simple container is the Calcium IoC container 
			 * that works accross all supported Calcium platforms. 
			 * It supports both inversion of control and dependency injection. 
			 * It can be accessed globally using the Dependency class. 
			 * The Dependency class leverages the Microsoft ServiceLocator, 
			 * allowing you to change the IoC container implementation. */
			var simpleContainer = new SimpleContainer();
			
			return simpleContainer;
		}

		protected virtual void PopulateContainer()
		{
			Dependency.Register<IProvider<ISynchronizationContext>>(() => new UIContextProvider(), true);
			Dependency.Register<ISynchronizationContext>(() => Dependency.Resolve<IProvider<ISynchronizationContext>>().ProvidedItem, true);

			RegisterBinarySerializer();
			RegisterLog();
			RegisterSettingsService();
			RegisterDialogService();
			RegisterMessenger();
			RegisterNavigationService();
			RegisterUserOptionsService();

#if WINDOWS_UWP || NETFX_CORE || WINDOWS_PHONE
			RegisterShareLinkLauncher();
			RegisterPhotoChooserLauncher();
			RegisterMarketplaceReviewLauncher();
			RegisterEmailComposeLauncher();
#endif

#if WINDOWS_UWP || NETFX_CORE || WINDOWS_PHONE || MONOTOUCH || MONODROID
			RegisterMarketplaceDetailLauncher();
#endif
		}

		protected virtual void RegisterBinarySerializer()
		{
			Dependency.Register<IBinarySerializer>(() => new BinarySerializer(), true);
		}

		protected virtual void RegisterSettingsService()
		{
			ISettingsStore localStore = new LocalSettingsStore();

#if !WPF && !WINDOWS_PHONE && !MONODROID && !__IOS__
			/* UWP doesn't include a transient state dictionary, so we use Calciums.*/
			var transientState = new TransientState();
			Dependency.Register<ITransientState>(transientState);

			var transientStore = new TransientSettingsStore(transientState);
#else
			var transientStore = new TransientSettingsStore();
#endif
			ISettingsStore roamingStore = null;
#if WINDOWS_PHONE || WINDOWS_UWP || NETFX_CORE
			roamingStore = new RoamingSettingsStore();			
#endif
			SettingsService settingsService = new SettingsService(
				// ReSharper disable once ExpressionIsAlwaysNull
				localStore, roamingStore, transientStore)
			{
				RaiseExceptionsOnConversionErrors = true
			};

			Dependency.Register<ISettingsService>(settingsService);
		}

		protected virtual void RegisterLog()
		{
			Dependency.Register<ILog>(() => new LocalStorageLog(), true);
		}

		protected virtual void RegisterDialogService()
		{
			//var dialogService = new DialogService
			//{
			//	/* Lambda expressions are used to retrieve the default message service captions, 
			//		* which allows the language to be changed at run-time. */
			//	//DefaultMessageCaptionFunc = () => Strings.DialogService_DefaultMessageCaption,
			//	//DefaultQuestionCaptionFunc = () => Strings.DialogService_DefaultQuestionCaption,
			//	//DefaultWarningCaptionFunc = () => Strings.DialogService_DefaultWarningCaption,
			//	//DefaultErrorCaptionFunc = () => Strings.DialogService_DefaultErrorCaption,
			//};
			Dependency.Register<IDialogService>(() => new DialogService(), true);
		}

		protected virtual void RegisterMessenger()
		{
			Dependency.Register<IMessenger>(() => new Messenger(), true);
		}

		protected virtual void RegisterNavigationService()
		{
#if __ANDROID__
			Dependency.Register<INavigationService>(() => new NavigationService(), true);
			Dependency.Register<INavigationRoutingService>(() => (INavigationRoutingService)Dependency.Resolve<INavigationService>(), true);
#else
			Dependency.Register<INavigationService>(() => new FrameNavigationService(), true);
#endif
		}

		protected virtual void RegisterUserOptionsService()
		{
			Dependency.Register<IUserOptionsService>(() => new UserOptionsService(), true);
		}

#if WINDOWS_UWP || NETFX_CORE || WINDOWS_PHONE
		protected virtual void RegisterShareLinkLauncher()
		{
			RegisterWithMockType<IShareLinkLauncher,
				ShareLinkLauncher, MockShareLinkLauncher>();
		}

		protected virtual void RegisterPhotoChooserLauncher()
		{
#if WINDOWS_PHONE
			RegisterWithMockType<IPhotoChooserLauncher,
				PhotoChooserAdapter, MockPhotoChooserLauncher>();
#else
			RegisterWithMockType<IPhotoChooserLauncher,
				PhotoChooserLauncher, MockPhotoChooserLauncher>();
#endif
		}

		protected virtual void RegisterMarketplaceReviewLauncher()
		{
			RegisterWithMockType<IMarketplaceReviewLauncher,
				MarketplaceReviewLauncher, MockMarketplaceReviewLauncher>();
		}

		protected virtual void RegisterEmailComposeLauncher()
		{
			RegisterWithMockType<IEmailComposeLauncher, 
				EmailComposeLauncher, MockEmailComposeLauncher>();
		}
#endif

#if WINDOWS_UWP || NETFX_CORE || WINDOWS_PHONE || MONOTOUCH || MONODROID
		protected virtual void RegisterMarketplaceDetailLauncher()
		{
			RegisterWithMockType<IMarketplaceDetailLauncher,
				MarketplaceDetailLauncher, MockMarketplaceDetailLauncher>();
		}
#endif

		void RegisterWithMockType<TService, TNotMock, TIfMock>() 
			where TNotMock : TService, new()
			where TIfMock : TService
		{
			if (initializationMode == InitializationMode.UnitTesting)
			{
				Dependency.Register<TService, TIfMock>(true);
			}
			else
			{
				Dependency.Register<TService>(() => new TNotMock(), true);
			}
		}
	}
}
